package lcmpt;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;

import org.marc4j.MarcReader;
import org.marc4j.MarcStreamReader;
import org.marc4j.marc.Record;

import de.dnb.basics.applicationComponents.StreamUtils;
import de.dnb.basics.statistics.ListMultimap;
import de.dnb.basics.statistics.Multimap;
import de.dnb.basics.statistics.TreeMultimap;
import de.dnb.basics.utils.MarcUtils;

public class Model {

	private final Map<String, Record> recordMap;
	private final Multimap<String, String> ubb;
	private final Set<String> topTerms;

	private DefaultMutableTreeNode theTree;
	private final Multimap<String, DefaultMutableTreeNode> nodes;
	private final InputStream inputStream;
	private final String path;

	public Model(InputStream inputStream, String path) throws IOException {
		recordMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
		ubb =
				new TreeMultimap<>(String.CASE_INSENSITIVE_ORDER,
						String.CASE_INSENSITIVE_ORDER);
		topTerms = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
		nodes = new ListMultimap<>();
		this.inputStream = inputStream;
		this.path = path;
		readRecordsIntoMap();
		connectRecords();
		makeJTree();
	}

	private void readRecordsIntoMap() throws FileNotFoundException {
		MarcReader reader;
		reader = new MarcStreamReader(inputStream);

		while (reader.hasNext()) {
			Record record = reader.next();
			String name = LCMPTMarcUtils.getPreferredName(record);
			recordMap.put(name, record);
		}
	}

	private void connectRecords() {
		Collection<String> names = getNames();
		for (String actualName : names) {
			Record marcRecord = recordMap.get(actualName);
			List<String> obb = LCMPTMarcUtils.getOBB(marcRecord);
			if (obb.isEmpty()) {
				topTerms.add(actualName);
			} else
				for (String ob : obb) {
					ubb.add(ob, actualName);
				}
		}
	}

	public Collection<String> getNames() {
		Set<String> names = recordMap.keySet();
		return names;
	}

	private void makeJTree() {
		// Wurzelknoten erstellen
		theTree = new DefaultMutableTreeNode(path);
		for (String topTerm : topTerms) {
			theTree.add(makeSubTree(topTerm));
		}

	}

	private DefaultMutableTreeNode makeSubTree(String name) {
		final DefaultMutableTreeNode node = new DefaultMutableTreeNode(name);
		nodes.add(name, node);
		Collection<String> ubbForName = ubb.getNullSafe(name);
		int index = 0;
		for (String ub : ubbForName) {
			DefaultMutableTreeNode ubNode = makeSubTree(ub);
			node.insert(ubNode, index);
			index++;
		}
		return node;
	}

	/**
	 * 
	 * @param name
	 *            beliebig
	 * @return UBB, nicht null
	 */
	public final Collection<String> getUBB(final String name) {
		return ubb.getNullSafe(name);
	}

	/**
	 * 
	 * @param name
	 *            beliebig
	 * @return nicht null
	 */
	public final Collection<TreeNode[]> getPaths(final String name) {
		Collection<DefaultMutableTreeNode> nodesForName = getNodes(name);
		Collection<TreeNode[]> nodeArrays = new LinkedList<>();
		for (DefaultMutableTreeNode mutableTreeNode : nodesForName) {
			nodeArrays.add(mutableTreeNode.getPath());
		}
		return nodeArrays;
	}

	/**
	 * 
	 * @param name
	 *            beliebig
	 * @return nicht null
	 */
	public final Collection<DefaultMutableTreeNode> getNodes(final String name) {
		Collection<DefaultMutableTreeNode> nodesForName = nodes.get(name);
		if (nodesForName != null)
			return nodesForName;
		else
			return new LinkedList<>();
	}

	public MutableTreeNode getTheTree() {
		return theTree;
	}

	/**
	 * 
	 * @param name	beliebig
	 * @return		name oder null
	 */
	public final Record getRecord(final String name) {
		return recordMap.get(name);
	}

}
